﻿#region Copyright
//       Copyright (c) 2009 - Antoine Jaussoin - http://www.jaussoin.com/blog/
// 
//        This program is free software: you can redistribute it and/or modify
//        it under the terms of the GNU General Public License as published by
//        the Free Software Foundation, either version 3 of the License, or
//        (at your option) any later version.
// 
//        This program is distributed in the hope that it will be useful,
//        but WITHOUT ANY WARRANTY; without even the implied warranty of
//        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//        GNU General Public License for more details.
// 
//        You should have received a copy of the GNU General Public License
//        along with this program.  If not, see <http://www.gnu.org/licenses/>.
#endregion
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;

namespace FT.Architecture.Entities
{
    /// <summary>
    /// Get information about the underlying storage
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public interface IUnderlyingStorage<T> : IGenericUnderlyingStorage<long, T> where T:IEntity
    {
    }

    /// <summary>
    /// Get information about the underlying storage
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <typeparam name="TId"></typeparam>
    public interface IGenericUnderlyingStorage<TId, T> where T:IGenericEntity<TId>
    {
        /// <summary>
        /// Returns information about the storage, from an entity member
        /// </summary>
        /// <param name="member"></param>
        /// <returns></returns>
        StorageInfo? GetStorageInfo(Expression<Func<T, object>> member);
        
        /// <summary>
        /// Returns information about the entity from a column name
        /// </summary>
        /// <param name="column"></param>
        /// <returns></returns>
        EntityInfo<TId, T>? GetEntityInfo(string column);
    }

    /// <summary>
    /// Underlying storage information
    /// </summary>
    public struct StorageInfo
    {
        private readonly string tableName;
        private readonly List<string> columnNames;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="tableName"></param>
        /// <param name="columnNames"></param>
        public StorageInfo(string tableName, params string[] columnNames)
        {
            this.tableName = tableName;
            this.columnNames = new List<string>(columnNames);
        }

        /// <summary>
        /// Table on which the entity is stored
        /// </summary>
        public string TableName
        {
            get { return tableName; }
        }

        /// <summary>
        /// Column (if one and only one) where the member is stored
        /// </summary>
        public string ColumnName
        {
            get { return columnNames.Count > 0 ? columnNames[0] : null; }
        }

        /// <summary>
        /// Collection of columns where the member is stored
        /// </summary>
        public IEnumerable<string> ColumnNames
        {
            get { return columnNames; }
        }

        /// <summary>
        /// Returns the fully qualified type name of this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"/> containing a fully qualified type name.
        /// </returns>
        /// <filterpriority>2</filterpriority>
        public override string ToString()
        {
            return tableName + "." + ColumnName ?? "MULTIPLE COLUMNS";
        }
    }

    /// <summary>
    /// Entity information
    /// </summary>
    public struct EntityInfo<TId, T> where T : IGenericEntity<TId>
    {
        private readonly Type entityType;
        private readonly MemberInfo entityMember;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="entityType"></param>
        /// <param name="entityMember"></param>
        public EntityInfo(Type entityType, MemberInfo entityMember)
        {
            this.entityType = entityType;
            this.entityMember = entityMember;
        }

        /// <summary>
        /// Type of the Entity
        /// </summary>
        public Type EntityType
        {
            get { return entityType; }
        }

        /// <summary>
        /// Information about the member
        /// </summary>
        public MemberInfo EntityMember
        {
            get { return entityMember; }
        }

        /// <summary>
        /// Lambda expression representing the member
        /// </summary>
        public Func<T, object> LambdaExpression
        {
            get
            {
                string memberName = entityMember.Name;
                return t => t.GetType().InvokeMember(memberName, BindingFlags.GetProperty, null, t, null);
            }
        }

        /// <summary>
        /// Returns the fully qualified type name of this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"/> containing a fully qualified type name.
        /// </returns>
        /// <filterpriority>2</filterpriority>
        public override string ToString()
        {
            return entityType.Name + "." + entityMember.Name;
        }
    }
}
